/*
 * Copyright (c) 2002 Florian Schulze.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the authors nor the names of the contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * ftpd.c - This file is part of the FTP daemon for lwIP
 *
 */

#include "lwip/debug.h"
#include "lwip/stats.h"
#include "ftp_server.h"
#include "lwip/tcp.h"
#include "ff.h"

#include <stdio.h>
#include <time.h>
#include <stdarg.h>
#include <dirent.h>
#include <ctype.h>
#include <string.h>
#include <sys/time.h>
#include <sys/stat.h>

#define	FTP_SERVER_DEBUG				LWIP_DBG_ON	
#define FTP_SERVER_DEBUG_TRACE        	(FTP_SERVER_DEBUG | LWIP_DBG_TRACE)
#define FTP_SERVER_DEBUG_STATE        	(FTP_SERVER_DEBUG | LWIP_DBG_STATE)
#define FTP_SERVER_DEBUG_WARN         	(FTP_SERVER_DEBUG | LWIP_DBG_LEVEL_WARNING)
#define FTP_SERVER_DEBUG_WARN_STATE   	(FTP_SERVER_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
#define FTP_SERVER_DEBUG_SERIOUS      	(FTP_SERVER_DEBUG | LWIP_DBG_LEVEL_SERIOUS)

/* 新文件指示器上的重启标记*/
#define msg110 "110 MARK %s = %s."
/* 服务器准备就绪的时间(分钟数)*/
#define msg120 "120 Service ready in nnn minutes."
/* 打开数据连接开始传输*/
#define msg125 "125 Data connection already open; transfer starting."
/* 打开连接*/
#define msg150 "150 File status okay; about to open data connection."
#define msg150recv "150 Opening BINARY mode data connection for %s (%i bytes)."
#define msg150stor "150 Opening BINARY mode data connection for %s."
/* 成功*/
#define msg200 "200 Command okay."
/* 命令没有执行*/
#define msg202 "202 Command not implemented, superfluous at this site."
/* 系统状态回复*/
#define msg211 "211 System status, or system help reply."
/* 目录状态回复*/
#define msg212 "212 Directory status."
/* 文件状态回复*/
#define msg213 "213 File status."
/* 帮助信息回复*/
#define msg214 "214 %s."
#define msg214SYST "214 %s system type."
/* 服务器就绪*/
#define msg220 "220 lwIP FTP Server ready."
/* 退出网络*/
#define msg221 "221 Goodbye."
/* 打开数据连接*/
#define msg225 "225 Data connection open; no transfer in progress."
/* 结束数据连接*/
#define msg226 "226 Closing data connection."
/* 进入被动模式(IP地址，ID端口)*/
#define msg227 "227 Entering Passive Mode (%i,%i,%i,%i,%i,%i)."
/* 用户登录*/
#define msg230 "230 User logged in, proceed."
/* 文件行为完成*/
#define msg250 "250 Requested file action okay, completed."
/* 路径名建立*/
#define msg257PWD "257 \"%s\" is current directory."
#define msg257 "257 \"%s\" created."
/* 要求密码*/
#define msg331 "331 User name okay, need password."
/* 要求账号*/
#define msg332 "332 Need account for login."
/* 文件行为暂停*/
#define msg350 "350 Requested file action pending further information."
/* 服务关闭*/
#define msg421 "421 Service not available, closing control connection."
/* 无法打开数据连接*/
#define msg425 "425 Can't open data connection."
/* 结束连接*/
#define msg426 "426 Connection closed; transfer aborted."
/* 文件不可用*/
#define msg450 "450 Requested file action not taken."
/* 遇到本地错误*/
#define msg451 "451 Requested action aborted: local error in processing."
/* 磁盘空间不足*/
#define msg452 "452 Requested action not taken."
/* 无效命令*/
#define msg500 "500 Syntax error, command unrecognized."
/* 错误参数*/
#define msg501 "501 Syntax error in parameters or arguments."
/* 命令没有执行*/
#define msg502 "502 Command not implemented."
/* 错误指令序列*/
#define msg503 "503 Bad sequence of commands."
/* 无效命令参数*/
#define msg504 "504 Command not implemented for that parameter."
/* 没有登录*/
#define msg530 "530 Not logged in."
/* 存储文件需要账号*/
#define msg532 "532 Need account for storing files."
/* 文件不可用*/
#define msg550 "550 Requested action not taken."
/* 不知道的页类型*/
#define msg551 "551 Requested action aborted: page type unknown."
/* 超过存储分配*/
#define msg552 "552 Requested file action aborted."
/* 文件名不允许*/
#define msg553 "553 Requested action not taken."

enum ftp_server_state_t {
	FTP_SERVER_USER,	/* 用户名*/
	FTP_SERVER_PASS,	/* 密码*/
	FTP_SERVER_IDLE,	/* 空闲*/
	FTP_SERVER_NLST,	/* 列出指定目录内容*/
	FTP_SERVER_LIST,	/* 如果是文件列出文件信息，目录则列出文件列表*/
	FTP_SERVER_RETR,	/* 从服务器上复制文件*/
	FTP_SERVER_RNFR,	/* 对旧路径重命名*/
	FTP_SERVER_STOR,	/* 复制文件到服务器*/
	FTP_SERVER_QUIT		/* 退出登录*/
};

static const char *month_table[12] = {
	"Jan",
	"Feb",
	"Mar",
	"Apr",
	"May",
	"Jun",
	"Jul",
	"Aug",
	"Sep",
	"Oct",
	"Nov",
	"Dez"
};

typedef int sfifo_atomic_t;
#ifdef __TURBOC__
#	define	SFIFO_MAX_BUFFER_SIZE	0x7fff
#else /* Kludge: Assume 32 bit platform */
#	define	SFIFO_MAX_BUFFER_SIZE	0x7fffffff
#endif

typedef struct sfifo_t
{
	char *buffer;
	int size;					/* Number of bytes */
	sfifo_atomic_t readpos;		/* Read position */
	sfifo_atomic_t writepos;	/* Write position */
} sfifo_t;

#define SFIFO_SIZEMASK(x)	((x)->size - 1)

#define sfifo_used(x)	(((x)->writepos - (x)->readpos) & SFIFO_SIZEMASK(x))

#define sfifo_space(x)	((x)->size - 1 - sfifo_used(x))

/* 分配缓存，初始化FIFO*/
static int sfifo_init(sfifo_t *f, int size)
{
	memset(f, 0, sizeof(sfifo_t));

	if(size > SFIFO_MAX_BUFFER_SIZE)
		return -EINVAL;

	f->size = 1;
	for(; f->size <= size; f->size <<= 1)
		;

	/* Get buffer */
	if( 0 == (f->buffer = (void *)malloc(f->size)) )
		return -ENOMEM;

	return 0;
}

/* 释放FIFO缓存*/
static void sfifo_close(sfifo_t *f)
{
	if(f->buffer)
		free(f->buffer);
}

/* 写数到FIFO*/
static int sfifo_write(sfifo_t *f, const void *_buf, int len)
{
	int total;
	int i;
	const char *buf = (const char *)_buf;

	if(!f->buffer)
		return -ENODEV;	/* No buffer! */

	total = sfifo_space(f);
	if(len > total)
		len = total;
	else
		total = len;

	i = f->writepos;
	if(i + len > f->size)
	{
		memcpy(f->buffer + i, buf, f->size - i);
		buf += f->size - i;
		len -= f->size - i;
		i = 0;
	}
	memcpy(f->buffer + i, buf, len);
	f->writepos = i + len;

	return total;
}

struct ftp_server_data_state {
	int connected;
	DIR *p_dir;
	struct dirent *p_dirent;
	FILE *p_file;
	sfifo_t fifo;
	struct tcp_pcb *msgpcb;
	struct ftp_server_msg_state *msgfs;
};

struct ftp_server_msg_state {
	enum ftp_server_state_t state;
	char cwd[1024];		/* 当前工作目录*/
	char path[1024];	/* 绝对路径缓存*/
	sfifo_t fifo;
	struct ip4_addr dataip;
	u16_t dataport;
	struct tcp_pcb *datapcb;
	struct ftp_server_data_state *datafs;
	int passive;
	char *renamefrom;
};

static void send_msg(struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm, char *msg, ...);

/* 改变当前目录到父目录*/
static void cd_up(struct ftp_server_msg_state *fsm)
{
	char *slash = NULL, *p;
	for(p = fsm->cwd; (*p)&&(p < fsm->cwd + sizeof(fsm->cwd) - 1); ++p)
	{
		if(*p == '/')	slash = p;
	}
	*slash = 0;
	if(strlen(fsm->cwd) == 0) {
		strcat(fsm->cwd, "/sdcard");
	}
}

/* 校验路径*/
static int vaildate_path(const char* path)
{
	const char* p;
	p = path;
	while((p = strstr(p, "/..")) != NULL) {
		if(p[3] == 0 || p[3] == '/') {
			return -1;
		}
	}
	if(strstr(path, "//") != NULL) {
		return -1;
	}
	return 0;
}

/* 获取相对cwd的绝对路径*/
static void build_path(const char* arg, struct ftp_server_msg_state* fsm)
{
	if(strstr(arg, "/sdcard") == NULL) {
		strcpy(fsm->path, fsm->cwd);
		if(arg[0] != '/') {
			strcat(fsm->path, "/");
		}
		strcat(fsm->path, arg);
	}else {
		strcpy(fsm->path, arg);
	}
}

/* 关闭数据连接*/
static void ftp_server_data_close(struct tcp_pcb *pcb, struct ftp_server_data_state *fsd)
{
	tcp_arg(pcb, NULL);
	tcp_sent(pcb, NULL);
	tcp_recv(pcb, NULL);
	fsd->msgfs->datafs = NULL;
	sfifo_close(&fsd->fifo);
	free(fsd);
	tcp_arg(pcb, NULL);
	tcp_close(pcb);
}

/* 发送数据*/
static void send_data(struct tcp_pcb *pcb, struct ftp_server_data_state *fsd)
{
	err_t err;
	u16_t len;

	if (sfifo_used(&fsd->fifo) > 0) {
		int i;

		/* 查看缓存池空间*/
		if (tcp_sndbuf(pcb) < sfifo_used(&fsd->fifo)) {
			len = tcp_sndbuf(pcb);
		} else {
			len = (u16_t) sfifo_used(&fsd->fifo);
		}
		/* 发送数据*/
		i = fsd->fifo.readpos;
		if ((i + len) > fsd->fifo.size) {
			err = tcp_write(pcb, fsd->fifo.buffer + i, (u16_t)(fsd->fifo.size - i), 1);
			if (err != ERR_OK) {
				LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("send_data: error writing!\n"));
				return;
			}
			len -= fsd->fifo.size - i;
			fsd->fifo.readpos = 0;
			i = 0;
		}

		err = tcp_write(pcb, fsd->fifo.buffer + i, len, 1);
		if (err != ERR_OK) {
			LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("send_data: error writing!\n"));
			return;
		}
		fsd->fifo.readpos += len;
	}
}

/* 发送文件*/
static void send_file(struct ftp_server_data_state *fsd, struct tcp_pcb *pcb)
{
	if (!fsd->connected)
		return;

	if (fsd->p_file) {
		char buffer[4096];
		int len;

		len = sfifo_space(&fsd->fifo);
		if (len == 0) {
			send_data(pcb, fsd);
			return;
		}
		if (len > 4096) {
			len = 4096;
		}
		len = fread(buffer, 1, len, fsd->p_file);
		if (len == 0) {
			if (feof(fsd->p_file) == 0)
				return;
			fclose(fsd->p_file);
			fsd->p_file = NULL;
			return;
		}
		sfifo_write(&fsd->fifo, buffer, len);
		send_data(pcb, fsd);
	} else {
		struct ftp_server_msg_state *fsm;
		struct tcp_pcb *msgpcb;

		if (sfifo_used(&fsd->fifo) > 0) {
			send_data(pcb, fsd);
			return;
		}
		fsm = fsd->msgfs;
		msgpcb = fsd->msgpcb;

		ftp_server_data_close(pcb, fsd);
		fsm->datapcb = NULL;
		fsm->datafs = NULL;
		fsm->state = FTP_SERVER_IDLE;
		send_msg(msgpcb, fsm, msg226);
		return;
	}
}

/* 发送下一个目录*/
static void send_next_directory(struct ftp_server_data_state *fsd, struct tcp_pcb *pcb, int shortlist)
{
	char buffer[1024];
	int len;

	while (1) {
		if (fsd->p_dirent == NULL)
			fsd->p_dirent = readdir(fsd->p_dir);

		if (fsd->p_dirent) {
			if (shortlist) {	// 短列表
				len = sprintf(buffer, "%s\r\n", fsd->p_dirent->d_name);
				if (sfifo_space(&fsd->fifo) < len) {
					send_data(pcb, fsd);
					return;
				}
				sfifo_write(&fsd->fifo, buffer, len);
				fsd->p_dirent = NULL;
			} else {			// 长列表
				struct stat st;
				struct tm* time;
				// 获取绝对地址
				build_path(fsd->p_dirent->d_name, fsd->msgfs);
				/* 获取文件状态*/
				stat(fsd->msgfs->path, &st);
				time = gmtime(&st.st_mtime);
				len = sprintf(buffer, "-rw-rw-rw-   1 user     ftp  %11ld %s %02i %02i:%02i %s\r\n", 
									st.st_size, month_table[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min, fsd->p_dirent->d_name);
				if (S_ISDIR(st.st_mode)) {
					buffer[0] = 'd';
				}
				if (sfifo_space(&fsd->fifo) < len) {
					send_data(pcb, fsd);
					return;
				}
				sfifo_write(&fsd->fifo, buffer, len);
				fsd->p_dirent = NULL;
			}
		} else {
			struct ftp_server_msg_state *fsm;
			struct tcp_pcb *msgpcb;

			if (sfifo_used(&fsd->fifo) > 0) {
				send_data(pcb, fsd);
				return;
			}
			fsm = fsd->msgfs;
			msgpcb = fsd->msgpcb;

			closedir(fsd->p_dir);
			fsd->p_dir = NULL;
			ftp_server_data_close(pcb, fsd);
			fsm->datapcb = NULL;
			fsm->datafs = NULL;
			fsm->state = FTP_SERVER_IDLE;
			send_msg(msgpcb, fsm, msg226);
			return;
		}
	}
}

static void data_state_process(struct ftp_server_data_state* fsd, struct tcp_pcb* pcb)
{
	switch (fsd->msgfs->state) {
	case FTP_SERVER_LIST:
		send_next_directory(fsd, pcb, 0);
		break;
	case FTP_SERVER_NLST:
		send_next_directory(fsd, pcb, 1);
		break;
	case FTP_SERVER_RETR:
		send_file(fsd, pcb);
		break;
	default:
		break;
	}
}

/* 数据错误回调*/
static void ftp_server_data_err(void *arg, err_t err)
{
	struct ftp_server_data_state *fsd = arg;

	LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("ftp_server_data_err: %s (%i)\n", lwip_strerr(err), err));
	if (fsd == NULL)
		return;
	fsd->msgfs->datafs = NULL;
	fsd->msgfs->state = FTP_SERVER_IDLE;
	free(fsd);
}

/* 发送数据回调*/
static err_t ftp_server_data_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
	struct ftp_server_data_state *fsd = arg;
	data_state_process(fsd, pcb);
	return ERR_OK;
}

/* 数据接收回调*/
static err_t ftp_server_data_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
	struct ftp_server_data_state *fsd = arg;

	if (err == ERR_OK && p != NULL) {
		struct pbuf *q;
		u16_t tot_len = 0;
		for (q = p; q != NULL; q = q->next) {
			int len;

			len = fwrite(q->payload, 1, q->len, fsd->p_file);
			tot_len += len;
			if (len != q->len)
				break;
		}

		/* 收到数据通知内核更新窗口 */
		tcp_recved(pcb, tot_len);
		/* 释放缓存 */
		pbuf_free(p);
	}
	if (err == ERR_OK && p == NULL) {
		struct ftp_server_msg_state *fsm;
		struct tcp_pcb *msgpcb;

		fsm = fsd->msgfs;
		msgpcb = fsd->msgpcb;

		fclose(fsd->p_file);
		fsd->p_file = NULL;
		ftp_server_data_close(pcb, fsd);
		fsm->datapcb = NULL;
		fsm->datafs = NULL;
		fsm->state = FTP_SERVER_IDLE;
		send_msg(msgpcb, fsm, msg226);
	}
	if(err != ERR_OK) {
		LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("recv err!!!"));
	}
	return ERR_OK;
}

/* 数据连接完成回调*/
static err_t ftp_server_data_connected(void *arg, struct tcp_pcb *pcb, err_t err)
{
	struct ftp_server_data_state *fsd = arg;

	fsd->msgfs->datapcb = pcb;
	fsd->connected = 1;

	// 数据回调函数
	tcp_recv(pcb, ftp_server_data_recv);

	// 发送回调函数
	tcp_sent(pcb, ftp_server_data_sent);

	// 错误回调函数
	tcp_err(pcb, ftp_server_data_err);

	// 状态处理
	data_state_process(fsd, pcb);

	return ERR_OK;
}

/* 数据连接回调*/
static err_t ftp_server_data_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
	struct ftp_server_data_state *fsd = arg;

	fsd->msgfs->datapcb = pcb;
	fsd->connected = 1;

	/* 数据接收回调 */
	tcp_recv(pcb, ftp_server_data_recv);

	/* 数据发送回调 */
	tcp_sent(pcb, ftp_server_data_sent);

	/* 错误回调*/
	tcp_err(pcb, ftp_server_data_err);

	data_state_process(fsd, pcb);

	return ERR_OK;
}

/* 打开数据连接*/
static int open_data_connection(struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	if (fsm->passive)
		return 0;

	/* 存储数据状态*/
	fsm->datafs = malloc(sizeof(struct ftp_server_data_state));
	if (fsm->datafs == NULL) {
		send_msg(pcb, fsm, msg451);
		return 1;
	}

	/* 状态结构初始化*/
	memset(fsm->datafs, 0, sizeof(struct ftp_server_data_state));
	fsm->datafs->msgfs = fsm;
	fsm->datafs->msgpcb = pcb;
	sfifo_init(&fsm->datafs->fifo, 4096);

	/* 新建数据TCP连接到客户端*/ 
	fsm->datapcb = tcp_new();
	tcp_arg(fsm->datapcb, fsm->datafs);
	ip_addr_t dataip;
	IP_SET_TYPE_VAL(dataip, IPADDR_TYPE_V4);
	ip4_addr_copy(*ip_2_ip4(&dataip), fsm->dataip);
	tcp_connect(fsm->datapcb, &dataip, fsm->dataport, ftp_server_data_connected);

	return 0;
}

static void cmd_user(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	send_msg(pcb, fsm, msg331);
	fsm->state = FTP_SERVER_PASS;
}

static void cmd_pass(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	send_msg(pcb, fsm, msg230);
	fsm->state = FTP_SERVER_IDLE;
}

static void cmd_port(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	int nr;
	unsigned pHi, pLo;
	unsigned ip[4];

	nr = sscanf(arg, "%u,%u,%u,%u,%u,%u", &(ip[0]), &(ip[1]), &(ip[2]), &(ip[3]), &pHi, &pLo);
	if (nr != 6) {
		send_msg(pcb, fsm, msg501);
	} else {
		IP4_ADDR(&fsm->dataip, (u8_t) ip[0], (u8_t) ip[1], (u8_t) ip[2], (u8_t) ip[3]);
		fsm->dataport = ((u16_t) pHi << 8) | (u16_t) pLo;
		send_msg(pcb, fsm, msg200);
	}
}

static void cmd_quit(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	send_msg(pcb, fsm, msg221);
	fsm->state = FTP_SERVER_QUIT;
}

/* 改变工作目录*/
static void cmd_cwd(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	// 校验路径
	if(vaildate_path(arg) != 0){
		send_msg(pcb, fsm, msg550);
		return;
	}
	// 获取绝对路径
	build_path(arg, fsm);
	// 更新路径
	strcpy(fsm->cwd, fsm->path);
	send_msg(pcb, fsm, msg250);
}

/* 进入上一层目录*/
static void cmd_cdup(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	cd_up(fsm);
	send_msg(pcb, fsm, msg250);
}

/* 显示当前目录*/
static void cmd_pwd(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	if (fsm->cwd) {
		send_msg(pcb, fsm, msg257PWD, fsm->cwd);
	}
}

static void cmd_list_common(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm, int shortlist)
{
	DIR *p_dir = NULL;

	if ((!fsm->cwd)) {
		send_msg(pcb, fsm, msg451);
		return;
	}
	// 打开当前目录
	p_dir = opendir(fsm->cwd);
	if (!p_dir) {
		send_msg(pcb, fsm, msg451);
		return;
	}
	//　打开数据连接
	if (open_data_connection(pcb, fsm) != 0) {
		closedir(p_dir);
		return;
	}

	fsm->datafs->p_dir = p_dir;
	fsm->datafs->p_dirent = NULL;
	if (shortlist != 0)
		fsm->state = FTP_SERVER_NLST;
	else
		fsm->state = FTP_SERVER_LIST;

	send_msg(pcb, fsm, msg150);
}

static void cmd_nlst(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	cmd_list_common(arg, pcb, fsm, 1);
}

static void cmd_list(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	cmd_list_common(arg, pcb, fsm, 0);
}

static void cmd_retr(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	FILE *p_file;
	struct stat st;
	// 获取绝对地址
	build_path(arg, fsm);
	// 获取文件属性
	stat(fsm->path, &st);
	
	if (!S_ISREG(st.st_mode)) {
		send_msg(pcb, fsm, msg550);
		return;
	}
	// 打开文件
	p_file = fopen(fsm->path, "rb");
	if (!p_file) {
		send_msg(pcb, fsm, msg550);
		return;
	}

	send_msg(pcb, fsm, msg150recv, fsm->path, st.st_size);
	// 打开数据连接
	if (open_data_connection(pcb, fsm) != 0) {
		fclose(p_file);
		return;
	}

	fsm->datafs->p_file = p_file;
	fsm->state = FTP_SERVER_RETR;
}

static void cmd_stor(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	FILE *p_file;
	// 获取绝对地址
	build_path(arg, fsm);
	// 打开文件
	p_file = fopen(fsm->path, "wb");
	if (!p_file) {
		send_msg(pcb, fsm, msg550);
		return;
	}

	send_msg(pcb, fsm, msg150stor, arg);
	// 打开数据连接
	if (open_data_connection(pcb, fsm) != 0) {
		fclose(p_file);
		return;
	}

	fsm->datafs->p_file = p_file;
	fsm->state = FTP_SERVER_STOR;
}

static void cmd_noop(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	send_msg(pcb, fsm, msg200);
}

static void cmd_syst(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	send_msg(pcb, fsm, msg214SYST, "UNIX");
}

static void cmd_pasv(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	static u16_t port = 4096;
	static u16_t start_port = 4096;
	struct tcp_pcb *temppcb;

	/* 为保持连接分配内存*/
	fsm->datafs = malloc(sizeof(struct ftp_server_data_state));

	if (fsm->datafs == NULL) {
		send_msg(pcb, fsm, msg451);
		return;
	}
	memset(fsm->datafs, 0, sizeof(struct ftp_server_data_state));

	fsm->datapcb = tcp_new();
	if (!fsm->datapcb) {
		free(fsm->datafs);
		send_msg(pcb, fsm, msg451);
		return;
	}

	sfifo_init(&fsm->datafs->fifo, 4096);

	start_port = port;

	while (1) {
		err_t err;

		if(++port > 0x7fff)
			port = 4096;

		fsm->dataport = port;
		err = tcp_bind(fsm->datapcb, (ip_addr_t*)&pcb->local_ip, fsm->dataport);
		if (err == ERR_OK)
			break;
		if (start_port == port)
			err = ERR_CLSD;
		if (err == ERR_USE) {
			continue;
		} else {
			ftp_server_data_close(fsm->datapcb, fsm->datafs);
			fsm->datapcb = NULL;
			fsm->datafs = NULL;
			return;
		}
	}

	fsm->datafs->msgfs = fsm;

	temppcb = tcp_listen(fsm->datapcb);
	if (!temppcb) {
		ftp_server_data_close(fsm->datapcb, fsm->datafs);
		fsm->datapcb = NULL;
		fsm->datafs = NULL;
		return;
	}
	fsm->datapcb = temppcb;

	fsm->passive = 1;
	fsm->datafs->connected = 0;
	fsm->datafs->msgpcb = pcb;

	/*  */
	tcp_arg(fsm->datapcb, fsm->datafs);

	tcp_accept(fsm->datapcb, ftp_server_data_accept);
	send_msg(pcb, fsm, msg227, ip4_addr1(ip_2_ip4(&pcb->local_ip)), ip4_addr2(ip_2_ip4(&pcb->local_ip)), ip4_addr3(ip_2_ip4(&pcb->local_ip)), ip4_addr4(ip_2_ip4(&pcb->local_ip)), (fsm->dataport >> 8) & 0xff, (fsm->dataport) & 0xff);
}

static void cmd_abrt(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	if (fsm->datafs != NULL) {
		tcp_arg(fsm->datapcb, NULL);
		tcp_sent(fsm->datapcb, NULL);
		tcp_recv(fsm->datapcb, NULL);
		tcp_arg(fsm->datapcb, NULL);
		tcp_abort(pcb);
		sfifo_close(&fsm->datafs->fifo);
		free(fsm->datafs);
		fsm->datafs = NULL;
	}
	fsm->state = FTP_SERVER_IDLE;
}

static void cmd_type(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("Got TYPE -%s-\n", arg));
	if(strcmp(arg, "I") != 0) {
		send_msg(pcb, fsm, msg502);
		return;
	}
	
	send_msg(pcb, fsm, msg200);
}

static void cmd_mode(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("Got MODE -%s-\n", arg));
	send_msg(pcb, fsm, msg502);
}

static void cmd_rnfr(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	if (arg == NULL) {
		send_msg(pcb, fsm, msg501);
		return;
	}
	if (*arg == '\0') {
		send_msg(pcb, fsm, msg501);
		return;
	}
	// 获取绝对地址
	build_path(arg, fsm);
	if (fsm->renamefrom)
		free(fsm->renamefrom);
	fsm->renamefrom = malloc(strlen(fsm->path) + 1);
	if (fsm->renamefrom == NULL) {
		send_msg(pcb, fsm, msg451);
		return;
	}
	strcpy(fsm->renamefrom, fsm->path);
	fsm->state = FTP_SERVER_RNFR;
	send_msg(pcb, fsm, msg350);
}

static void cmd_rnto(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	if (fsm->state != FTP_SERVER_RNFR) {
		send_msg(pcb, fsm, msg503);
		return;
	}
	fsm->state = FTP_SERVER_IDLE;
	if (arg == NULL) {
		send_msg(pcb, fsm, msg501);
		return;
	}
	if (*arg == '\0') {
		send_msg(pcb, fsm, msg501);
		return;
	}
	// 获取绝对路径
	build_path(arg, fsm);
	// 重命名
	if (rename(fsm->renamefrom, fsm->path)) {
		send_msg(pcb, fsm, msg450);
	} else {
		send_msg(pcb, fsm, msg250);
	}
}

static void cmd_mkd(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	if (arg == NULL) {
		send_msg(pcb, fsm, msg501);
		return;
	}
	if (*arg == '\0') {
		send_msg(pcb, fsm, msg501);
		return;
	}
	// 获取绝对地址
	build_path(arg, fsm);
	if (mkdir(fsm->path, S_IRWXU | S_IRWXG | S_IRWXO) != 0) {
		send_msg(pcb, fsm, msg550);
	} else {
		send_msg(pcb, fsm, msg257, arg);
	}
}

static void cmd_rmd(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	struct stat st;

	if (arg == NULL) {
		send_msg(pcb, fsm, msg501);
		return;
	}
	if (*arg == '\0') {
		send_msg(pcb, fsm, msg501);
		return;
	}
	// 获取绝对地址
	build_path(arg, fsm);
	// 获取文件状态
	if (stat(fsm->path, &st) != 0) {
		send_msg(pcb, fsm, msg550);
		return;
	}
	if (!S_ISDIR(st.st_mode)) {
		send_msg(pcb, fsm, msg550);
		return;
	}
	if (rmdir(fsm->path) != 0) {
		send_msg(pcb, fsm, msg550);
	} else {
		send_msg(pcb, fsm, msg250);
	}
}

static void cmd_dele(const char *arg, struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	struct stat st;

	if (arg == NULL) {
		send_msg(pcb, fsm, msg501);
		return;
	}
	if (*arg == '\0') {
		send_msg(pcb, fsm, msg501);
		return;
	}
	// 获取绝对地址
	build_path(arg, fsm);
	// 获取文件状态
	if (stat(fsm->path, &st) != 0) {
		send_msg(pcb, fsm, msg550);
		return;
	}
	if (!S_ISREG(st.st_mode)) {
		send_msg(pcb, fsm, msg550);
		return;
	}
	// 删除文件
	if (remove(fsm->path) != 0) {
		send_msg(pcb, fsm, msg550);
	} else {
		send_msg(pcb, fsm, msg250);
	}
}

struct ftp_server_command {
	char *cmd;
	void (*func) (const char *arg, struct tcp_pcb * pcb, struct ftp_server_msg_state * fsm);
};

static struct ftp_server_command ftp_server_commands[] = {
	{"USER", cmd_user},	/* 系统登录的用户名*/
	{"PASS", cmd_pass},	/* 系统登录密码*/
	{"PORT", cmd_port},	/* IP地址和两字节端口ID*/
	{"QUIT", cmd_quit},	/* 退出登录*/
	{"CWD", cmd_cwd},	/* 改变服务器工作目录*/
	{"CDUP", cmd_cdup},	/* 改变服务器上的父目录*/
	{"PWD", cmd_pwd},	/* 显示当前工作目录*/
	{"XPWD", cmd_pwd},	/* */
	{"NLST", cmd_nlst},	/* 列出指定目录内容*/
	{"LIST", cmd_list},	/* 如果是文件名列出文件信息，如果是目录则列出文件列表*/
	{"RETR", cmd_retr},	/* 从服务器上复制文件*/
	{"STOR", cmd_stor},	/* 复制文件到服务器*/
	{"NOOP", cmd_noop},	/* 无动作*/
	{"SYST", cmd_syst},	/* 返回服务器上使用的操作系统*/
	{"ABOR", cmd_abrt},	/* 中断数据连接*/
	{"TYPE", cmd_type},	/* 数据类型*/
	{"MODE", cmd_mode},	/* 传输模式*/
	{"RNFR", cmd_rnfr},	/* 对旧路径重命名*/
	{"RNTO", cmd_rnto},	/* 对新路径重命名*/
	{"MKD", cmd_mkd},	/* 在服务器上建立指定目录*/
	{"XMKD", cmd_mkd},	/* */
	{"RMD", cmd_rmd},	/* 在服务器上删除指定目录*/
	{"XRMD", cmd_rmd},	/* */
	{"DELE", cmd_dele},	/* 删除服务器上的指定文件*/
	//{"PASV", cmd_pasv},
	{NULL, NULL}
};

/* 发送消息数据*/
static void send_msg_data(struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	err_t err;
	u16_t len;

	if (sfifo_used(&fsm->fifo) > 0) {
		int i;

		if (tcp_sndbuf(pcb) < sfifo_used(&fsm->fifo)) {
			len = tcp_sndbuf(pcb);
		} else {
			len = (u16_t) sfifo_used(&fsm->fifo);
		}

		i = fsm->fifo.readpos;
		if ((i + len) > fsm->fifo.size) {
			err = tcp_write(pcb, fsm->fifo.buffer + i, (u16_t)(fsm->fifo.size - i), 1);
			if (err != ERR_OK) {
				LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("send_msg_data: error writing!\n"));
				return;
			}
			len -= fsm->fifo.size - i;
			fsm->fifo.readpos = 0;
			i = 0;
		}

		err = tcp_write(pcb, fsm->fifo.buffer + i, len, 1);
		if (err != ERR_OK) {
			LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("send_msg_data: error writing!\n"));
			return;
		}
		fsm->fifo.readpos += len;
	}
}

/* 发送消息*/
static void send_msg(struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm, char *msg, ...)
{
	va_list arg;
	char buffer[512];
	int len;

	va_start(arg, msg);
	vsprintf(buffer, msg, arg);
	va_end(arg);
	strcat(buffer, "\r\n");
	len = strlen(buffer);
	if (sfifo_space(&fsm->fifo) < len)
		return;
	sfifo_write(&fsm->fifo, buffer, len);
	LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("response: %s", buffer));
	send_msg_data(pcb, fsm);
}

/* 消息错误回调*/
static void ftp_server_msg_err(void *arg, err_t err)
{
	struct ftp_server_msg_state *fsm = arg;

	LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("ftp_server_msg_err: %s (%i)\n", lwip_strerr(err), err));
	if (fsm == NULL)
		return;
	if (fsm->datafs)
		ftp_server_data_close(fsm->datapcb, fsm->datafs);
	sfifo_close(&fsm->fifo);
	if (fsm->renamefrom)
		free(fsm->renamefrom);
	fsm->renamefrom = NULL;
	free(fsm);
}

/* 关闭消息连接*/
static void ftp_server_msg_close(struct tcp_pcb *pcb, struct ftp_server_msg_state *fsm)
{
	tcp_arg(pcb, NULL);
	tcp_sent(pcb, NULL);
	tcp_recv(pcb, NULL);
	if (fsm->datafs)
		ftp_server_data_close(fsm->datapcb, fsm->datafs);
	sfifo_close(&fsm->fifo);
	if (fsm->renamefrom)
		free(fsm->renamefrom);
	fsm->renamefrom = NULL;
	free(fsm);
	tcp_arg(pcb, NULL);
	tcp_close(pcb);
}

/* 消息发送回调*/
static err_t ftp_server_msg_sent(void *arg, struct tcp_pcb *pcb, u16_t len)
{
	struct ftp_server_msg_state *fsm = arg;

	if ((sfifo_used(&fsm->fifo) == 0) && (fsm->state == FTP_SERVER_QUIT)) {
		ftp_server_msg_close(pcb, fsm);
		return ERR_OK;
	}
	
	if (pcb->state <= ESTABLISHED) send_msg_data(pcb, fsm);
	return ERR_OK;
}

/* 接收消息回调*/
static err_t ftp_server_msg_recv(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
	char *text;
	struct ftp_server_msg_state *fsm = arg;

	if (err == ERR_OK && p != NULL) {

		text = malloc(p->tot_len + 1);
		if (text) {
			char cmd[5];
			struct pbuf *q;
			char *pt = text;
			struct ftp_server_command *ftp_server_cmd;
			/* 数据拷贝 */
			for (q = p; q != NULL; q = q->next) {
				bcopy(q->payload, pt, q->len);
				pt += q->len;
			}
			*pt = '\0';
			/* 过滤换行符 */
			pt = &text[strlen(text) - 1];
			while (((*pt == '\r') || (*pt == '\n')) && pt >= text)
				*pt-- = '\0';

			LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("query: %s\n", text));
			/* 获取命令并转为大写字母 */
			strncpy(cmd, text, 4);
			for (pt = cmd; isalpha((int8_t)*pt) && pt < &cmd[4]; pt++)
				*pt = toupper((int8_t)*pt);
			*pt = '\0';
			/* 命令判断 */
			for (ftp_server_cmd = ftp_server_commands; ftp_server_cmd->cmd != NULL; ftp_server_cmd++) {
				if (!strcmp(ftp_server_cmd->cmd, cmd))
					break;
			}

			if (strlen(text) < (strlen(cmd) + 1))
				pt = "";
			else
				pt = &text[strlen(cmd) + 1];
			/* 命令处理 */
			if (ftp_server_cmd->func)
				ftp_server_cmd->func(pt, pcb, fsm);
			else
				send_msg(pcb, fsm, msg502);

			free(text);
		}
		/* 获取数据后通知内核更新窗口 */
		tcp_recved(pcb, p->tot_len);
		/* 释放缓存*/
		pbuf_free(p);
	} else if (err == ERR_OK && p == NULL) {
	    ftp_server_msg_close(pcb, fsm);
	}

	return ERR_OK;
}

/* 消息轮询回调*/
static err_t ftp_server_msg_poll(void *arg, struct tcp_pcb *pcb)
{
	struct ftp_server_msg_state *fsm = arg;

	if(fsm == NULL)
		return ERR_OK;

	if((fsm->datafs)&&(fsm->datafs->connected)) {
		data_state_process(fsm->datafs, fsm->datapcb);
	}

	return ERR_OK;
}

/* 消息连接回调*/
static err_t ftp_server_msg_accept(void *arg, struct tcp_pcb *pcb, err_t err)
{
	struct ftp_server_msg_state *fsm;

	/* 为新的连接分配内存 */
	fsm = malloc(sizeof(struct ftp_server_msg_state));

	if (fsm == NULL) {
		LWIP_DEBUGF(FTP_SERVER_DEBUG_TRACE, ("ftp_server_msg_accept: Out of memory\n"));
		return ERR_MEM;
	}
	memset(fsm, 0, sizeof(struct ftp_server_msg_state));

	/* 初始化结构体 */
	sfifo_init(&fsm->fifo, 512);
	memset(fsm->cwd, 0, sizeof(fsm->cwd));
	memset(fsm->path, 0, sizeof(fsm->path));
	strcpy(fsm->cwd, "/sdcard");
	fsm->state = FTP_SERVER_IDLE;

	/* 为回调函数设置上下文 */
	tcp_arg(pcb, fsm);

	/* 设置接收回调函数 */
	tcp_recv(pcb, ftp_server_msg_recv);

	/* 设置发送回调函数 */
	tcp_sent(pcb, ftp_server_msg_sent);

	/* 设置错误回调函数 */
	tcp_err(pcb, ftp_server_msg_err);

	/* 设置轮询回调函数 */
	tcp_poll(pcb, ftp_server_msg_poll, 1);

	/* 发送服务器准备好消息 */ 
	send_msg(pcb, fsm, msg220);

	return ERR_OK;
}

/* 初始化*/
void ftp_server_init(void)
{
	struct tcp_pcb *pcb;
	// 创建TCP
	pcb = tcp_new();
	// 绑定端口
	tcp_bind(pcb, IP_ADDR_ANY, 21);
	// 设置监听
	pcb = tcp_listen(pcb);
	// 等待连接
	tcp_accept(pcb, ftp_server_msg_accept);
}
